<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多媒体消息测试</title>
    <link rel="stylesheet" href="css/websocket-test.css">
    <style>
        .media-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 15px;
            margin-top: 10px;
        }
        .media-item {
            border: 1px solid #ddd;
            border-radius: 8px;
            padding: 10px;
            display: flex;
            flex-direction: column;
            align-items: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            transition: transform 0.2s;
            background-color: #fff;
        }
        .media-item:hover {
            transform: translateY(-3px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.15);
        }
        .media-preview-container {
            width: 100%;
            height: 140px;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
            margin-bottom: 10px;
            background-color: #f5f5f5;
            border-radius: 4px;
            position: relative;
        }
        .media-preview-container img {
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
        }
        .media-preview-container video {
            max-width: 100%;
            max-height: 100%;
        }
        .media-preview-container audio {
            width: 100%;
        }
        .media-item-buttons {
            width: 100%;
            display: flex;
            justify-content: center;
            margin-top: 8px;
            gap: 6px;
        }
        .media-item-buttons button {
            padding: 4px 10px;
            font-size: 12px;
            border-radius: 4px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
            cursor: pointer;
        }
        .media-item-buttons button:hover {
            background-color: #e0e0e0;
        }
        .media-item-buttons .btn-primary {
            background-color: #007bff;
            color: white;
            border-color: #0069d9;
        }
        .media-item-buttons .btn-primary:hover {
            background-color: #0069d9;
        }
        .media-info {
            width: 100%;
            font-size: 12px;
            color: #666;
            margin: 5px 0;
        }
        .media-info .sender {
            font-weight: bold;
            color: #444;
        }
        .media-info .timestamp {
            color: #999;
        }
        .file-icon {
            font-size: 48px;
            color: #607d8b;
        }
        .file-icon-pdf {
            color: #f44336;
        }
        .file-icon-excel {
            color: #4caf50;
        }
        .file-icon-word {
            color: #2196f3;
        }
        .file-icon-image {
            color: #ff9800;
        }
        .file-icon-audio {
            color: #9c27b0;
        }
        .file-icon-video {
            color: #e91e63;
        }
        .file-preview-frame {
            border: 1px solid #ddd;
            border-radius: 4px;
            background: white;
            padding: 2px;
            width: 90%;
            height: 90%;
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
        }
        .preview-iframe {
            width: 100%;
            height: 100%;
            border: none;
        }
        .document-preview {
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background: #f9f9f9;
        }
        .modal-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.8);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 1000;
            flex-direction: column;
        }
        .modal-content {
            width: 90%;
            height: 80%;
            background: white;
            border-radius: 8px;
            overflow: hidden;
            position: relative;
        }
        .modal-header {
            padding: 10px;
            background: #f0f0f0;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #ddd;
        }
        .modal-body {
            height: calc(100% - 50px);
            overflow: auto;
        }
        .preview-controls {
            width: 100%;
            display: flex;
            justify-content: center;
            margin-top: 10px;
            gap: 10px;
        }
        .progress-container {
            width: 100%;
            height: 6px;
            background-color: #f0f0f0;
            border-radius: 3px;
            margin: 5px 0;
            overflow: hidden;
        }
        .progress-bar {
            height: 100%;
            background-color: #4CAF50;
            width: 0%;
            transition: width 0.3s;
        }
        .audio-preview-container {
            background-color: #eef2f7;
            width: 100%;
            border-radius: 8px;
            padding: 8px;
        }
        .audio-waveform {
            height: 60px;
            background: linear-gradient(to bottom, #8e44ad 0%, #3498db 100%);
            opacity: 0.6;
            border-radius: 4px;
            clip-path: polygon(0% 50%, 5% 45%, 10% 55%, 15% 65%, 20% 40%, 25% 45%, 30% 60%, 35% 70%, 40% 30%, 45% 40%, 50% 55%, 55% 65%, 60% 45%, 65% 35%, 70% 50%, 75% 60%, 80% 35%, 85% 45%, 90% 55%, 95% 50%, 100% 60%, 100% 100%, 0% 100%);
        }
        .video-duration, .audio-duration {
            position: absolute;
            bottom: 5px;
            right: 5px;
            background-color: rgba(0,0,0,0.7);
            color: white;
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 12px;
        }
        .file-preview {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
        }
        .file-ext {
            font-size: 14px;
            font-weight: bold;
            margin-top: 5px;
            text-transform: uppercase;
        }
        /* 进度条动画 */
        @keyframes progress {
            0% { width: 0%; }
            100% { width: 100%; }
        }
        .animate-progress {
            animation: progress 1.5s ease-in-out;
        }
        /* 成功完成动画 */
        @keyframes success-pulse {
            0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.4); }
            50% { transform: scale(1.03); box-shadow: 0 0 0 10px rgba(76, 175, 80, 0); }
            100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); }
        }
        .animate-success {
            animation: success-pulse 1s ease-in-out;
            border-color: #4CAF50;
        }
        .text-center {
            text-align: center;
            width: 100%;
            font-size: 12px;
            margin-top: 4px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>多媒体消息测试</h1>
        <div class="notification notification-info">
            此页面用于测试图片、语音、视频等多媒体内容的发送和接收
        </div>

        <div class="row">
            <div class="col" style="max-width: 300px;">
                <div class="card">
                    <h2 class="card-header">连接设置</h2>
                    <div class="form-group">
                        <label for="endpoint">WebSocket端点</label>
                        <input type="text" id="endpoint" value="/ChatMessageServer" placeholder="例如 /ChatMessageServer">
                    </div>
                    <div class="form-group">
                        <label for="username">您的用户名</label>
                        <input type="text" id="username" placeholder="输入用户名">
                    </div>
                    <div class="form-group">
                        <button id="connectBtn" class="btn w-100">连接</button>
                        <button id="disconnectBtn" class="btn btn-secondary w-100 mt-2" disabled>断开</button>
                    </div>

                    <div class="mt-3">
                        <div class="d-flex align-items-center">
                            <div id="statusIndicator" class="status-indicator status-offline"></div>
                            <span id="statusText">未连接</span>
                        </div>
                    </div>
                </div>

                <div class="card mt-3">
                    <h2 class="card-header">联系人</h2>
                    <div class="form-group">
                        <label for="receiver">接收者</label>
                        <input type="text" id="receiver" placeholder="输入接收者用户名">
                    </div>
                </div>
            </div>

            <div class="col" style="flex-grow: 2;">
                <div class="card">
                    <h2 class="card-header">媒体上传</h2>
                    <div class="tabs">
                        <div class="tab active" data-tab="image">图片</div>
                        <div class="tab" data-tab="audio">语音</div>
                        <div class="tab" data-tab="video">视频</div>
                        <div class="tab" data-tab="file">文件</div>
                    </div>

                    <div class="tab-content active" id="imageTab">
                        <div class="form-group">
                            <label for="imageFile">选择图片</label>
                            <input type="file" id="imageFile" accept="image/*">
                        </div>
                        <div class="media-preview" id="imagePreview"></div>
                        <button id="sendImageBtn" class="btn" disabled>发送图片</button>
                    </div>

                    <div class="tab-content" id="audioTab">
                        <div class="form-group">
                            <label>录制语音</label>
                            <div>
                                <button id="recordAudioBtn" class="btn btn-secondary">开始录音</button>
                                <button id="stopAudioBtn" class="btn" disabled>停止录音</button>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="audioFile">或选择语音文件</label>
                            <input type="file" id="audioFile" accept="audio/*">
                        </div>
                        <div class="media-preview" id="audioPreview"></div>
                        <button id="sendAudioBtn" class="btn" disabled>发送语音</button>
                    </div>

                    <div class="tab-content" id="videoTab">
                        <div class="form-group">
                            <label>录制视频</label>
                            <div>
                                <button id="recordVideoBtn" class="btn btn-secondary">开始录制</button>
                                <button id="stopVideoBtn" class="btn" disabled>停止录制</button>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="videoFile">或选择视频文件</label>
                            <input type="file" id="videoFile" accept="video/*">
                        </div>
                        <div class="media-preview" id="videoPreview"></div>
                        <button id="sendVideoBtn" class="btn" disabled>发送视频</button>
                    </div>

                    <div class="tab-content" id="fileTab">
                        <div class="form-group">
                            <label for="fileInput">选择文件</label>
                            <input type="file" id="fileInput">
                        </div>
                        <div class="file-info" id="fileInfo"></div>
                        <button id="sendFileBtn" class="btn" disabled>发送文件</button>
                    </div>
                </div>

                <div class="card mt-3">
                    <h2 class="card-header">发送与接收的媒体</h2>
                    <div class="media-grid" id="mediaContainer">
                        <!-- 媒体内容将在这里显示 -->
                        <div class="text-muted text-center w-100 p-5">尚无媒体内容</div>
                    </div>
                </div>
            </div>
        </div>

        <div class="row mt-3">
            <div class="col">
                <div class="card">
                    <h2 class="card-header d-flex justify-content-between align-items-center">
                        <span>日志</span>
                        <button id="clearLogsBtn" class="btn btn-small btn-secondary">清空日志</button>
                    </h2>
                    <div id="logContainer" class="message-list" style="height: 200px;"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- 脚本引入 -->
    <script src="js/websocket-client.js"></script>
    <script>
    // DOM元素
    const connectBtn = document.getElementById('connectBtn');
    const disconnectBtn = document.getElementById('disconnectBtn');
    const endpointInput = document.getElementById('endpoint');
    const usernameInput = document.getElementById('username');
    const receiverInput = document.getElementById('receiver');
    const statusIndicator = document.getElementById('statusIndicator');
    const statusText = document.getElementById('statusText');
    const logContainer = document.getElementById('logContainer');
    const clearLogsBtn = document.getElementById('clearLogsBtn');
    const mediaContainer = document.getElementById('mediaContainer');

    // 图片上传相关
    const imageFileInput = document.getElementById('imageFile');
    const imagePreview = document.getElementById('imagePreview');
    const sendImageBtn = document.getElementById('sendImageBtn');

    // 音频相关
    const recordAudioBtn = document.getElementById('recordAudioBtn');
    const stopAudioBtn = document.getElementById('stopAudioBtn');
    const audioFileInput = document.getElementById('audioFile');
    const audioPreview = document.getElementById('audioPreview');
    const sendAudioBtn = document.getElementById('sendAudioBtn');

    // 视频相关
    const recordVideoBtn = document.getElementById('recordVideoBtn');
    const stopVideoBtn = document.getElementById('stopVideoBtn');
    const videoFileInput = document.getElementById('videoFile');
    const videoPreview = document.getElementById('videoPreview');
    const sendVideoBtn = document.getElementById('sendVideoBtn');

    // 文件相关
    const fileInput = document.getElementById('fileInput');
    const fileInfo = document.getElementById('fileInfo');
    const sendFileBtn = document.getElementById('sendFileBtn');

    // 标签页相关
    const tabs = document.querySelectorAll('.tab');
    const tabContents = document.querySelectorAll('.tab-content');

    // 变量
    let wsClient = null;
    let mediaRecorder = null;
    let recordedChunks = [];
    let audioStream = null;
    let videoStream = null;
    let currentAudioBlob = null;
    let currentVideoBlob = null;
    let currentImageData = null;
    let currentFile = null;

    // 页面初始化
    function init() {
        // 从URL参数获取用户名和端点
        const urlParams = new URLSearchParams(window.location.search);
        if (urlParams.has('username')) {
            usernameInput.value = urlParams.get('username');
        }
        if (urlParams.has('endpoint')) {
            endpointInput.value = urlParams.get('endpoint');
        }
        if (urlParams.has('receiver')) {
            receiverInput.value = urlParams.get('receiver');
        }

        // 绑定事件
        connectBtn.addEventListener('click', connect);
        disconnectBtn.addEventListener('click', disconnect);
        clearLogsBtn.addEventListener('click', clearLogs);
        
        // 图片相关事件
        imageFileInput.addEventListener('change', handleImageSelect);
        sendImageBtn.addEventListener('click', sendImage);

        // 音频相关事件
        recordAudioBtn.addEventListener('click', startAudioRecording);
        stopAudioBtn.addEventListener('click', stopAudioRecording);
        audioFileInput.addEventListener('change', handleAudioSelect);
        sendAudioBtn.addEventListener('click', sendAudio);

        // 视频相关事件
        recordVideoBtn.addEventListener('click', startVideoRecording);
        stopVideoBtn.addEventListener('click', stopVideoRecording);
        videoFileInput.addEventListener('change', handleVideoSelect);
        sendVideoBtn.addEventListener('click', sendVideo);

        // 文件相关事件
        fileInput.addEventListener('change', handleFileSelect);
        sendFileBtn.addEventListener('click', sendFile);

        // 标签页切换
        tabs.forEach(tab => {
            tab.addEventListener('click', () => {
                tabs.forEach(t => t.classList.remove('active'));
                tabContents.forEach(c => c.classList.remove('active'));
                
                tab.classList.add('active');
                document.getElementById(tab.getAttribute('data-tab') + 'Tab').classList.add('active');
            });
        });
    }

    // 连接WebSocket
    function connect() {
        const endpoint = endpointInput.value.trim();
        const username = usernameInput.value.trim();

        if (!endpoint) {
            addLog('错误', 'WebSocket端点不能为空');
            return;
        }

        if (!username) {
            addLog('错误', '用户名不能为空');
            return;
        }

        // 禁用连接按钮
        connectBtn.disabled = true;
        addLog('信息', `尝试连接到 ${endpoint}...`);

        try {
            // 创建WebSocket客户端
            wsClient = new WebSocketClient(endpoint, username, {
                onConnect: handleConnect,
                onDisconnect: handleDisconnect,
                onMessage: handleMessage,
                onError: handleError,
                onReconnect: handleReconnect,
                onReconnectFailed: handleReconnectFailed
            });

            // 连接
            wsClient.connect();
        } catch (error) {
            addLog('错误', `连接初始化失败: ${error.message}`);
            connectBtn.disabled = false;
        }
    }

    // 处理连接成功
    function handleConnect() {
        // 更新UI状态
        statusIndicator.className = 'status-indicator status-online';
        statusText.textContent = '已连接';
        connectBtn.disabled = true;
        disconnectBtn.disabled = false;
        
        addLog('成功', '连接已建立');
    }

    // 处理连接断开
    function handleDisconnect(event) {
        // 更新UI状态
        statusIndicator.className = 'status-indicator status-offline';
        statusText.textContent = '已断开';
        connectBtn.disabled = false;
        disconnectBtn.disabled = true;
        sendImageBtn.disabled = true;
        sendAudioBtn.disabled = true;
        sendVideoBtn.disabled = true;
        sendFileBtn.disabled = true;

        // 停止所有媒体流
        stopAllMediaStreams();

        addLog('信息', `连接已断开${event.code ? (' (代码: ' + event.code + ')') : ''}`);
    }

    // 处理接收消息
    function handleMessage(message) {
        addLog('收到', '接收到消息: ' + JSON.stringify(message));
        
        // 处理多媒体消息
        if (message.type === 'chat' && message.contentType) {
            // 如果是文件类型，尝试进一步识别具体文件类型
            if (message.contentType === 'file' && message.fileName) {
                const fileExt = getFileExtension(message.fileName).toLowerCase();
                
                // 基于扩展名进一步识别文件类型
                if (['pdf'].includes(fileExt)) {
                    displayReceivedMedia(message, 'file', 'pdf');
                } else if (['xlsx', 'xls', 'csv'].includes(fileExt)) {
                    displayReceivedMedia(message, 'file', 'excel');
                } else if (['doc', 'docx', 'rtf'].includes(fileExt)) {
                    displayReceivedMedia(message, 'file', 'word');
                } else {
                    displayReceivedMedia(message, 'file');
                }
            } else {
                // 正常处理其他媒体类型
                switch (message.contentType) {
                    case 'image':
                        displayReceivedMedia(message, 'image');
                        break;
                    case 'audio':
                        displayReceivedMedia(message, 'audio');
                        break;
                    case 'video':
                        displayReceivedMedia(message, 'video');
                        break;
                    case 'file':
                        displayReceivedMedia(message, 'file');
                        break;
                }
            }
        } else if (message.type === 'media_transfer') {
            // 处理分块媒体传输
            const transferId = message.transferId || '';
            const chunkIndex = message.chunkIndex;
            const totalChunks = message.totalChunks;
            const senderId = message.fromUser;
            const progress = Math.round(((chunkIndex + 1) / totalChunks) * 100);
            
            addLog('信息', `接收到媒体分块: ${chunkIndex + 1}/${totalChunks} (${progress}%)`);
            
            // 更新或创建进度条
            updateTransferProgress(transferId, senderId, message.contentType || 'file', progress, totalChunks === chunkIndex + 1);
            
            // 如果是最后一个分块，在数据组装完成后显示媒体
            if (totalChunks === chunkIndex + 1) {
                // 实际应用中，这里应该等待所有块组装完成
                // 在组装完成后调用 displayReceivedMedia
                setTimeout(() => {
                    addLog('成功', `已完成媒体接收: ${message.fileName || '文件'}`);
                    
                    // 如果是文件，尝试判断文件类型
                    if (message.contentType === 'file' && message.fileName) {
                        const fileExt = getFileExtension(message.fileName).toLowerCase();
                        if (['pdf'].includes(fileExt)) {
                            // 处理PDF类型
                            addLog('信息', '识别为PDF文档');
                        } else if (['xlsx', 'xls', 'csv'].includes(fileExt)) {
                            // 处理Excel类型
                            addLog('信息', '识别为Excel表格');
                        } else if (['doc', 'docx', 'rtf'].includes(fileExt)) {
                            // 处理Word类型
                            addLog('信息', '识别为Word文档');
                        }
                    }
                }, 500);
            }
        }
    }

    // 更新传输进度条
    function updateTransferProgress(transferId, senderId, mediaType, progress, isComplete) {
        // 查找或创建传输进度显示项
        let transferItem = document.getElementById(`transfer_${transferId}`);
        
        if (!transferItem && !isComplete) {
            // 如果不存在且不是完成状态，创建一个新的进度显示
            transferItem = document.createElement('div');
            transferItem.id = `transfer_${transferId}`;
            transferItem.className = 'media-item';
            
            const mediaTypeName = getMediaTypeName(mediaType);
            
            transferItem.innerHTML = `
                <div class="media-preview-container">
                    <div class="file-preview">
                        <i class="file-icon">⏬</i>
                        <div>接收中...</div>
                    </div>
                </div>
                <div class="media-info">
                    <span class="sender">从 ${senderId} 接收</span> ${mediaTypeName}
                </div>
                <div class="progress-container">
                    <div class="progress-bar" id="progress_${transferId}" style="width:${progress}%"></div>
                </div>
                <div id="progress_text_${transferId}" class="text-center">${progress}%</div>
            `;
            
            // 清除"无媒体"提示
            if (mediaContainer.querySelector('.text-muted.text-center.w-100')) {
                mediaContainer.innerHTML = '';
            }
            
            // 添加到容器
            mediaContainer.appendChild(transferItem);
        } else if (transferItem && !isComplete) {
            // 更新现有进度条
            const progressBar = document.getElementById(`progress_${transferId}`);
            const progressText = document.getElementById(`progress_text_${transferId}`);
            
            if (progressBar) {
                progressBar.style.width = `${progress}%`;
            }
            
            if (progressText) {
                progressText.textContent = `${progress}%`;
            }
        } else if (transferItem && isComplete) {
            // 完成时添加完成动画或标记
            const progressBar = document.getElementById(`progress_${transferId}`);
            const progressText = document.getElementById(`progress_text_${transferId}`);
            
            if (progressBar) {
                progressBar.style.width = '100%';
                progressBar.style.backgroundColor = '#4caf50'; // 绿色表示完成
            }
            
            if (progressText) {
                progressText.textContent = '100% - 完成';
            }
            
            // 添加一个动画效果表示完成
            transferItem.classList.add('animate-success');
            
            // 在实际应用中，这里可能需要根据媒体类型更新显示
        }
    }

    // 处理错误
    function handleError(error) {
        addLog('错误', error.message || '发生未知错误');
    }

    // 处理重连尝试
    function handleReconnect(attempt, maxAttempts) {
        statusText.textContent = `正在重连 (${attempt}/${maxAttempts})`;
        statusText.className = 'animate-blink';
        addLog('信息', `尝试重连 (${attempt}/${maxAttempts})...`);
    }

    // 处理重连失败
    function handleReconnectFailed() {
        statusText.textContent = '重连失败';
        statusText.className = '';
        connectBtn.disabled = false;
        addLog('错误', '重连失败，已达到最大尝试次数');
    }

    // 主动断开连接
    function disconnect() {
        stopAllMediaStreams();
        if (wsClient) {
            wsClient.disconnect();
        }
    }

    // 停止所有媒体流
    function stopAllMediaStreams() {
        if (audioStream) {
            audioStream.getTracks().forEach(track => track.stop());
            audioStream = null;
        }
        
        if (videoStream) {
            videoStream.getTracks().forEach(track => track.stop());
            videoStream = null;
        }
    }

    // 验证接收者
    function validateReceiver() {
        const receiver = receiverInput.value.trim();
        if (!receiver) {
            addLog('错误', '请输入接收者用户名');
            return false;
        }
        return true;
    }

    //=====================
    // 图片处理相关
    //=====================
    
    // 处理图片选择
    function handleImageSelect(e) {
        const file = e.target.files[0];
        if (!file) return;
        
        if (!file.type.startsWith('image/')) {
            addLog('错误', '请选择有效的图片文件');
            return;
        }
        
        // 读取并预览图片
        FileUtil.readFileAsBase64(file).then(data => {
            currentImageData = data;
            
            // 显示预览
            imagePreview.innerHTML = `<img src="${data}" alt="Image Preview">`;
            sendImageBtn.disabled = !wsClient || !wsClient.connected;
        }).catch(error => {
            addLog('错误', '读取图片失败: ' + error.message);
        });
    }
    
    // 发送图片
    function sendImage() {
        if (!wsClient || !wsClient.connected) {
            addLog('错误', '未连接到服务器');
            return;
        }
        
        if (!validateReceiver() || !currentImageData) return;
        
        const receiver = receiverInput.value.trim();
        
        // 发送图片消息
        wsClient.sendMediaMessage(receiver, 'image', currentImageData);
        addLog('发送', '已发送图片');
        
        // 显示在媒体列表中
        displaySentMedia('image', currentImageData);
    }
    
    //=====================
    // 音频处理相关
    //=====================
    
    // 开始录制音频
    function startAudioRecording() {
        if (audioStream) {
            addLog('警告', '录音已在进行中');
            return;
        }
        
        // 请求音频权限
        navigator.mediaDevices.getUserMedia({ audio: true })
            .then(stream => {
                audioStream = stream;
                recordAudioBtn.disabled = true;
                stopAudioBtn.disabled = false;
                
                // 设置录音器
                recordedChunks = [];
                mediaRecorder = new MediaRecorder(stream);
                
                mediaRecorder.ondataavailable = e => {
                    if (e.data.size > 0) {
                        recordedChunks.push(e.data);
                    }
                };
                
                mediaRecorder.onstop = () => {
                    // 合并录音数据
                    const audioBlob = new Blob(recordedChunks, { type: 'audio/webm' });
                    currentAudioBlob = audioBlob;
                    
                    // 创建预览
                    const audioURL = URL.createObjectURL(audioBlob);
                    audioPreview.innerHTML = `<audio controls src="${audioURL}"></audio>`;
                    
                    // 将Blob转换为Base64
                    const reader = new FileReader();
                    reader.onloadend = () => {
                        const base64data = reader.result;
                        sendAudioBtn.disabled = !wsClient || !wsClient.connected;
                    };
                    reader.readAsDataURL(audioBlob);
                    
                    // 重置UI
                    recordAudioBtn.disabled = false;
                    stopAudioBtn.disabled = true;
                };
                
                // 开始录音
                mediaRecorder.start();
                addLog('信息', '开始录音');
            })
            .catch(error => {
                addLog('错误', '无法访问麦克风: ' + error.message);
            });
    }
    
    // 停止录制音频
    function stopAudioRecording() {
        if (!mediaRecorder) return;
        
        mediaRecorder.stop();
        addLog('信息', '录音已停止');
        
        // 停止所有音频轨道
        if (audioStream) {
            audioStream.getTracks().forEach(track => track.stop());
            audioStream = null;
        }
    }
    
    // 处理音频文件选择
    function handleAudioSelect(e) {
        const file = e.target.files[0];
        if (!file) return;
        
        if (!file.type.startsWith('audio/')) {
            addLog('错误', '请选择有效的音频文件');
            return;
        }
        
        // 读取并预览音频
        const audioURL = URL.createObjectURL(file);
        audioPreview.innerHTML = `<audio controls src="${audioURL}"></audio>`;
        
        // 读取为Base64
        FileUtil.readFileAsBase64(file).then(data => {
            currentAudioBlob = file;
            sendAudioBtn.disabled = !wsClient || !wsClient.connected;
        }).catch(error => {
            addLog('错误', '读取音频失败: ' + error.message);
        });
    }
    
    // 发送音频
    function sendAudio() {
        if (!wsClient || !wsClient.connected) {
            addLog('错误', '未连接到服务器');
            return;
        }
        
        if (!validateReceiver() || !currentAudioBlob) return;
        
        const receiver = receiverInput.value.trim();
        
        // 读取音频为Base64
        FileUtil.readFileAsBase64(currentAudioBlob).then(data => {
            // 发送音频消息
            wsClient.sendMediaMessage(receiver, 'audio', data);
            addLog('发送', '已发送音频');
            
            // 显示在媒体列表中
            displaySentMedia('audio', data);
        }).catch(error => {
            addLog('错误', '准备音频失败: ' + error.message);
        });
    }
    
    //=====================
    // 视频处理相关
    //=====================
    
    // 开始录制视频
    function startVideoRecording() {
        if (videoStream) {
            addLog('警告', '视频录制已在进行中');
            return;
        }
        
        // 请求视频和音频权限
        navigator.mediaDevices.getUserMedia({ video: true, audio: true })
            .then(stream => {
                videoStream = stream;
                recordVideoBtn.disabled = true;
                stopVideoBtn.disabled = false;
                
                // 预览视频流
                const videoElement = document.createElement('video');
                videoElement.srcObject = stream;
                videoElement.autoplay = true;
                videoElement.muted = true; // 避免反馈
                videoPreview.innerHTML = '';
                videoPreview.appendChild(videoElement);
                
                // 设置录像器
                recordedChunks = [];
                mediaRecorder = new MediaRecorder(stream);
                
                mediaRecorder.ondataavailable = e => {
                    if (e.data.size > 0) {
                        recordedChunks.push(e.data);
                    }
                };
                
                mediaRecorder.onstop = () => {
                    // 合并视频数据
                    const videoBlob = new Blob(recordedChunks, { type: 'video/webm' });
                    currentVideoBlob = videoBlob;
                    
                    // 创建预览
                    const videoURL = URL.createObjectURL(videoBlob);
                    videoPreview.innerHTML = `<video controls src="${videoURL}"></video>`;
                    
                    // 重置UI
                    recordVideoBtn.disabled = false;
                    stopVideoBtn.disabled = true;
                    sendVideoBtn.disabled = !wsClient || !wsClient.connected;
                };
                
                // 开始录像
                mediaRecorder.start();
                addLog('信息', '开始录制视频');
            })
            .catch(error => {
                addLog('错误', '无法访问摄像头: ' + error.message);
            });
    }
    
    // 停止录制视频
    function stopVideoRecording() {
        if (!mediaRecorder) return;
        
        mediaRecorder.stop();
        addLog('信息', '视频录制已停止');
        
        // 停止所有视频轨道
        if (videoStream) {
            videoStream.getTracks().forEach(track => track.stop());
            videoStream = null;
        }
    }
    
    // 处理视频文件选择
    function handleVideoSelect(e) {
        const file = e.target.files[0];
        if (!file) return;
        
        if (!file.type.startsWith('video/')) {
            addLog('错误', '请选择有效的视频文件');
            return;
        }
        
        // 读取并预览视频
        const videoURL = URL.createObjectURL(file);
        videoPreview.innerHTML = `<video controls src="${videoURL}" style="max-width:100%;"></video>`;
        
        currentVideoBlob = file;
        sendVideoBtn.disabled = !wsClient || !wsClient.connected;
    }
    
    // 发送视频
    function sendVideo() {
        if (!wsClient || !wsClient.connected) {
            addLog('错误', '未连接到服务器');
            return;
        }
        
        if (!validateReceiver() || !currentVideoBlob) return;
        
        const receiver = receiverInput.value.trim();
        addLog('信息', '正在准备视频数据...');
        
        // 读取视频为Base64
        FileUtil.readFileAsBase64(currentVideoBlob).then(data => {
            // 视频文件通常较大，可能需要分块发送
            if (data.length > 100000) { // 大于~100KB时分块
                addLog('信息', '视频较大，使用分块传输');
                
                // 分块传输视频
                FileUtil.splitFileIntoChunks(currentVideoBlob).then(chunks => {
                    const fileName = 'video_' + Date.now() + '.webm';
                    
                    // 发送分块
                    wsClient.sendLargeFile(
                        receiver,
                        'video',
                        chunks,
                        fileName,
                        '.webm',
                        currentVideoBlob.size
                    );
                    
                    addLog('发送', `已开始发送视频 (${chunks.length}个分块)`);
                    
                    // 显示在媒体列表中
                    displaySentMedia('video', data);
                });
            } else {
                // 小视频直接发送
                wsClient.sendMediaMessage(receiver, 'video', data);
                addLog('发送', '已发送视频');
                
                // 显示在媒体列表中
                displaySentMedia('video', data);
            }
        }).catch(error => {
            addLog('错误', '准备视频失败: ' + error.message);
        });
    }
    
    //=====================
    // 文件处理相关
    //=====================
    
    // 处理文件选择
    function handleFileSelect(e) {
        const file = e.target.files[0];
        if (!file) return;
        
        currentFile = file;
        
        // 显示文件信息
        const fileSizeKB = Math.round(file.size / 1024);
        fileInfo.innerHTML = `
            <div><strong>文件名:</strong> ${file.name}</div>
            <div><strong>类型:</strong> ${file.type || '未知'}</div>
            <div><strong>大小:</strong> ${fileSizeKB} KB</div>
        `;
        
        sendFileBtn.disabled = !wsClient || !wsClient.connected;
    }
    
    // 发送文件
    function sendFile() {
        if (!wsClient || !wsClient.connected) {
            addLog('错误', '未连接到服务器');
            return;
        }
        
        if (!validateReceiver() || !currentFile) return;
        
        const receiver = receiverInput.value.trim();
        addLog('信息', '正在准备文件...');
        
        // 获取文件扩展名
        let fileExtension = '';
        const fileNameParts = currentFile.name.split('.');
        if (fileNameParts.length > 1) {
            fileExtension = '.' + fileNameParts.pop();
        }
        
        // 处理大文件分块发送
        if (currentFile.size > 1024 * 1024) { // 大于1MB分块
            addLog('信息', '文件较大，使用分块传输');
            
            FileUtil.splitFileIntoChunks(currentFile).then(chunks => {
                wsClient.sendLargeFile(
                    receiver,
                    'file',
                    chunks,
                    currentFile.name,
                    fileExtension,
                    currentFile.size
                );
                
                addLog('发送', `已开始发送文件 ${currentFile.name} (${chunks.length}个分块)`);
            }).catch(error => {
                addLog('错误', '准备文件分块失败: ' + error.message);
            });
        } else {
            // 小文件直接发送
            FileUtil.readFileAsBase64(currentFile).then(data => {
                wsClient.sendMediaMessage(
                    receiver,
                    'file',
                    data,
                    currentFile.name,
                    fileExtension,
                    currentFile.size
                );
                
                addLog('发送', `已发送文件 ${currentFile.name}`);
            }).catch(error => {
                addLog('错误', '准备文件失败: ' + error.message);
            });
        }
    }
    
    //=====================
    // 媒体展示相关
    //=====================
    
    // 显示已发送的媒体
    function displaySentMedia(mediaType, mediaData, fileName) {
        const mediaItem = document.createElement('div');
        mediaItem.className = 'media-item';
        
        const timestamp = new Date().toLocaleTimeString();
        const fileSize = getFileSizeFromBase64(mediaData);
        
        // 创建进度条
        const progressContainer = document.createElement('div');
        progressContainer.className = 'progress-container';
        const progressBar = document.createElement('div');
        progressBar.className = 'progress-bar animate-progress';
        progressBar.style.width = '100%';
        progressContainer.appendChild(progressBar);
        
        // 准备预览区域
        let mediaPreview = '';
        let additionalInfo = '';
        
        switch (mediaType) {
            case 'image':
                mediaPreview = `<img src="${mediaData}" alt="Sent Image">`;
                break;
                
            case 'audio':
                const audioId = 'audio_' + Date.now();
                mediaPreview = `
                    <div class="audio-preview-container">
                        <div class="audio-waveform"></div>
                        <audio id="${audioId}" controls src="${mediaData}" onloadedmetadata="updateMediaDuration(this, '${audioId}_duration')"></audio>
                        <span id="${audioId}_duration" class="audio-duration">00:00</span>
                    </div>
                `;
                break;
                
            case 'video':
                const videoId = 'video_' + Date.now();
                mediaPreview = `
                    <video id="${videoId}" controls src="${mediaData}" onloadedmetadata="updateMediaDuration(this, '${videoId}_duration')"></video>
                    <span id="${videoId}_duration" class="video-duration">00:00</span>
                `;
                break;
                
            case 'file':
                const fileExt = getFileExtension(currentFile?.name || 'file');
                mediaPreview = `
                    <div class="file-preview">
                        <i class="file-icon">📄</i>
                        <div class="file-ext">${fileExt}</div>
                    </div>
                `;
                additionalInfo = `<div><strong>文件名:</strong> ${currentFile?.name || '未知'}</div>`;
                break;
        }
        
        mediaItem.innerHTML = `
            <div class="media-preview-container">${mediaPreview}</div>
            <div class="media-info">
                <span class="sender">已发送</span> ${getMediaTypeName(mediaType)}
                <span class="timestamp">${timestamp}</span>
                <div>${fileSize ? '大小: ' + fileSize : ''}</div>
                ${additionalInfo}
            </div>
            ${progressContainer.outerHTML}
            <div class="media-item-buttons">
                <button class="btn btn-small" onclick="saveMedia('${mediaType}', '${encodeURIComponent(mediaData)}', '${currentFile?.name || `sent_${mediaType}`}')">保存</button>
                <button class="btn btn-small btn-primary" onclick="resendMedia('${mediaType}')">重发</button>
            </div>
        `;
        
        // 清除"无媒体"提示
        if (mediaContainer.querySelector('.text-muted.text-center.w-100')) {
            mediaContainer.innerHTML = '';
        }
        
        // 添加到容器
        mediaContainer.appendChild(mediaItem);
    }
    
    // 显示已接收的媒体
    function displayReceivedMedia(message, mediaType, specificFileType) {
        const mediaItem = document.createElement('div');
        mediaItem.className = 'media-item';
        
        const timestamp = new Date(message.timestamp || Date.now()).toLocaleTimeString();
        const fileSize = getFileSizeFromBase64(message.content);
        const fileName = message.fileName || `received_${mediaType}${getDefaultExtension(mediaType)}`;
        const fileExt = getFileExtension(fileName).toLowerCase();
        
        // 确定更具体的文件类型
        let specificType = specificFileType || mediaType;
        let iconClass = '';
        
        // 处理文档类型
        if (mediaType === 'file' && !specificFileType) {
            if (['pdf'].includes(fileExt)) {
                specificType = 'pdf';
                iconClass = 'file-icon-pdf';
            } else if (['xlsx', 'xls', 'csv'].includes(fileExt)) {
                specificType = 'excel';
                iconClass = 'file-icon-excel';
            } else if (['doc', 'docx', 'rtf'].includes(fileExt)) {
                specificType = 'word';
                iconClass = 'file-icon-word';
            } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'].includes(fileExt)) {
                specificType = 'image';
                iconClass = 'file-icon-image';
            } else if (['mp3', 'wav', 'ogg', 'aac', 'm4a'].includes(fileExt)) {
                specificType = 'audio';
                iconClass = 'file-icon-audio';
            } else if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'].includes(fileExt)) {
                specificType = 'video';
                iconClass = 'file-icon-video';
            }
        } else if (specificFileType) {
            // 使用传入的文件类型
            switch (specificFileType) {
                case 'pdf': 
                    iconClass = 'file-icon-pdf';
                    break;
                case 'excel': 
                    iconClass = 'file-icon-excel';
                    break;
                case 'word': 
                    iconClass = 'file-icon-word';
                    break;
            }
        }
        
        // 准备预览区域
        let mediaPreview = '';
        let additionalInfo = '';
        
        switch (mediaType) {
            case 'image':
                mediaPreview = `<img src="${message.content}" alt="Received Image">`;
                break;
                
            case 'audio':
                const audioId = 'audio_' + Date.now();
                mediaPreview = `
                    <div class="audio-preview-container">
                        <div class="audio-waveform"></div>
                        <audio id="${audioId}" controls src="${message.content}" onloadedmetadata="updateMediaDuration(this, '${audioId}_duration')"></audio>
                        <span id="${audioId}_duration" class="audio-duration">00:00</span>
                    </div>
                `;
                break;
                
            case 'video':
                const videoId = 'video_' + Date.now();
                mediaPreview = `
                    <video id="${videoId}" controls src="${message.content}" onloadedmetadata="updateMediaDuration(this, '${videoId}_duration')"></video>
                    <span id="${videoId}_duration" class="video-duration">00:00</span>
                `;
                break;
                
            case 'file':
                mediaPreview = `
                    <div class="file-preview">
                        <div class="file-preview-frame">
                            <i class="file-icon ${iconClass || ''}">
                                ${getFileIcon(specificType)}
                            </i>
                            <div class="file-ext">${fileExt.toUpperCase()}</div>
                        </div>
                    </div>
                `;
                additionalInfo = `<div><strong>文件名:</strong> ${fileName}</div>`;
                break;
        }
        
        // 添加操作按钮
        let actionButtons = '';
        switch (mediaType) {
            case 'image':
                actionButtons = `
                    <button class="btn btn-small btn-primary" onclick="saveMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">保存</button>
                    <button class="btn btn-small" onclick="previewMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">预览</button>
                `;
                break;
            case 'audio':
                actionButtons = `
                    <button class="btn btn-small btn-primary" onclick="saveMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">保存</button>
                    <button class="btn btn-small" onclick="previewMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">播放</button>
                `;
                break;
            case 'video':
                actionButtons = `
                    <button class="btn btn-small btn-primary" onclick="saveMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">保存</button>
                    <button class="btn btn-small" onclick="previewMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">播放</button>
                `;
                break;
            case 'file':
                actionButtons = `
                    <button class="btn btn-small btn-primary" onclick="saveMedia('${mediaType}', '${encodeURIComponent(message.content)}', '${fileName}')">保存</button>
                `;
                
                // 根据文件类型添加不同的预览按钮
                if (['pdf', 'image', 'excel', 'word'].includes(specificType)) {
                    actionButtons += `<button class="btn btn-small" onclick="previewMedia('${specificType}', '${encodeURIComponent(message.content)}', '${fileName}')">预览</button>`;
                }
                break;
        }
        
        mediaItem.innerHTML = `
            <div class="media-preview-container">
                ${mediaPreview}
            </div>
            <div class="media-info">
                <span class="sender">从 ${message.fromUser} 接收</span> ${getMediaTypeName(specificType)}
                <span class="timestamp">${timestamp}</span>
                <div>${fileSize ? '大小: ' + fileSize : ''}</div>
                ${additionalInfo}
            </div>
            <div class="media-item-buttons">
                ${actionButtons}
            </div>
        `;
        
        // 清除"无媒体"提示
        if (mediaContainer.querySelector('.text-muted.text-center.w-100')) {
            mediaContainer.innerHTML = '';
        }
        
        // 添加到容器
        mediaContainer.appendChild(mediaItem);
    }
    
    // 从Base64估算文件大小
    function getFileSizeFromBase64(base64String) {
        if (!base64String) return '';
        // 去掉header部分，如 data:image/png;base64,
        const base64 = base64String.split(',').pop();
        if (!base64) return '';
        
        // 估算文件大小: Base64字符串长度 * 3 / 4 (因为是Base64编码)
        const sizeInBytes = Math.round(base64.length * 3 / 4);
        
        if (sizeInBytes < 1024) {
            return `${sizeInBytes} B`;
        } else if (sizeInBytes < 1024 * 1024) {
            return `${(sizeInBytes / 1024).toFixed(1)} KB`;
        } else {
            return `${(sizeInBytes / (1024 * 1024)).toFixed(1)} MB`;
        }
    }
    
    // 获取文件扩展名
    function getFileExtension(filename) {
        if (!filename) return '';
        const parts = filename.split('.');
        if (parts.length <= 1) return '';
        return parts.pop().toUpperCase();
    }
    
    // 获取默认扩展名
    function getDefaultExtension(mediaType) {
        switch (mediaType) {
            case 'image': return '.jpg';
            case 'audio': return '.mp3';
            case 'video': return '.mp4';
            default: return '';
        }
    }
    
    // 更新媒体时长
    function updateMediaDuration(mediaElement, durationElementId) {
        const duration = mediaElement.duration;
        const minutes = Math.floor(duration / 60);
        const seconds = Math.floor(duration % 60);
        document.getElementById(durationElementId).innerText = 
            `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
    }
    
    // 保存媒体
    function saveMedia(mediaType, encodedData, fileName) {
        try {
            const data = decodeURIComponent(encodedData);
            const link = document.createElement('a');
            link.href = data;
            link.download = fileName || `download_${Date.now()}${getDefaultExtension(mediaType)}`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            
            addLog('成功', `已保存 ${getMediaTypeName(mediaType)}`);
        } catch(error) {
            addLog('错误', `保存失败: ${error.message}`);
        }
    }
    
    // 预览媒体
    function previewMedia(mediaType, encodedData, fileName) {
        try {
            const data = decodeURIComponent(encodedData);
            
            // 创建模态窗口
            const modal = document.createElement('div');
            modal.className = 'modal-container';
            
            const modalContent = document.createElement('div');
            modalContent.className = 'modal-content';
            
            // 添加标题栏
            const modalHeader = document.createElement('div');
            modalHeader.className = 'modal-header';
            modalHeader.innerHTML = `
                <span>${fileName}</span>
                <button class="btn btn-small">关闭</button>
            `;
            
            // 添加内容区
            const modalBody = document.createElement('div');
            modalBody.className = 'modal-body';
            
            // 根据媒体类型创建不同的预览
            switch(mediaType) {
                case 'image':
                    modalBody.innerHTML = `<img src="${data}" style="max-width:100%; max-height:100%;">`;
                    break;
                    
                case 'audio':
                    modalBody.innerHTML = `
                        <div style="padding:20px; text-align:center;">
                            <div class="audio-waveform" style="height:100px; margin-bottom:20px;"></div>
                            <audio controls src="${data}" autoplay style="width:80%;"></audio>
                        </div>
                    `;
                    break;
                    
                case 'video':
                    modalBody.innerHTML = `
                        <video controls src="${data}" autoplay style="width:100%; height:100%;"></video>
                    `;
                    break;
                    
                case 'pdf':
                    // 对于PDF，我们可以尝试在iframe中预览
                    if (data.startsWith('data:application/pdf')) {
                        modalBody.innerHTML = `<iframe src="${data}" class="preview-iframe"></iframe>`;
                    } else {
                        modalBody.innerHTML = `
                            <div class="document-preview">
                                <div class="file-icon file-icon-pdf" style="font-size:80px;">📑</div>
                                <p>PDF文件无法直接预览</p>
                            </div>
                        `;
                    }
                    break;
                    
                case 'excel':
                    modalBody.innerHTML = `
                        <div class="document-preview">
                            <div class="file-icon file-icon-excel" style="font-size:80px;">📊</div>
                            <p>Excel文件无法直接预览</p>
                        </div>
                    `;
                    break;
                    
                case 'word':
                    modalBody.innerHTML = `
                        <div class="document-preview">
                            <div class="file-icon file-icon-word" style="font-size:80px;">📝</div>
                            <p>Word文件无法直接预览</p>
                        </div>
                    `;
                    break;
                    
                default:
                    modalBody.innerHTML = `
                        <div class="document-preview">
                            <div class="file-icon" style="font-size:80px;">📄</div>
                            <p>此类型文件无法直接预览</p>
                        </div>
                    `;
            }
            
            // 将所有部分组合在一起
            modalContent.appendChild(modalHeader);
            modalContent.appendChild(modalBody);
            modal.appendChild(modalContent);
            
            // 添加控制按钮
            const controls = document.createElement('div');
            controls.className = 'preview-controls';
            controls.innerHTML = `
                <button class="btn" onclick="saveMedia('${mediaType}', '${encodedData}', '${fileName}')">保存文件</button>
            `;
            modal.appendChild(controls);
            
            // 添加关闭事件
            const closeBtn = modalHeader.querySelector('button');
            closeBtn.addEventListener('click', () => {
                document.body.removeChild(modal);
            });
            
            // 显示模态窗口
            document.body.appendChild(modal);
            
        } catch(error) {
            addLog('错误', `预览失败: ${error.message}`);
        }
    }
    
    // 获取文件图标
    function getFileIcon(fileType) {
        switch(fileType) {
            case 'pdf': return '📑';
            case 'excel': return '📊';
            case 'word': return '📝';
            case 'image': return '🖼️';
            case 'audio': return '🎵';
            case 'video': return '🎬';
            case 'file':
            default: return '📄';
        }
    }
    
    // 重发媒体
    function resendMedia(mediaType) {
        if (!wsClient || !wsClient.connected) {
            addLog('错误', '未连接到服务器');
            return;
        }
        
        const receiver = receiverInput.value.trim();
        if (!receiver) {
            addLog('错误', '请输入接收者用户名');
            return;
        }
        
        switch (mediaType) {
            case 'image':
                if (currentImageData) sendImage();
                break;
            case 'audio':
                if (currentAudioBlob) sendAudio();
                break;
            case 'video':
                if (currentVideoBlob) sendVideo();
                break;
            case 'file':
                if (currentFile) sendFile();
                break;
        }
    }
    
    // 获取媒体类型的中文名称
    function getMediaTypeName(mediaType) {
        switch (mediaType) {
            case 'image': return '图片';
            case 'audio': return '语音';
            case 'video': return '视频';
            case 'pdf': return 'PDF文档';
            case 'excel': return 'Excel表格';
            case 'word': return 'Word文档';
            case 'file': return '文件';
            default: return '媒体';
        }
    }
    
    //=====================
    // 日志相关
    //=====================
    
    // 添加日志
    function addLog(level, message) {
        const logItem = document.createElement('div');
        logItem.className = 'message-item animate-fade-in';
        
        const timeStr = new Date().toLocaleTimeString();
        let levelClass = '';
        
        switch (level.toLowerCase()) {
            case '错误':
                levelClass = 'text-danger';
                break;
            case '成功':
                levelClass = 'text-success';
                break;
            case '信息':
                levelClass = 'text-muted';
                break;
            case '收到':
                levelClass = 'text-success';
                break;
            case '发送':
                levelClass = 'text-primary';
                break;
            default:
                levelClass = '';
        }
        
        logItem.innerHTML = `
            <div class="message-header">
                <span class="message-sender ${levelClass}">[${level}]</span>
                <span class="message-time">${timeStr}</span>
            </div>
            <div class="message-content">${message}</div>
        `;
        
        logContainer.appendChild(logItem);
        logContainer.scrollTop = logContainer.scrollHeight;
    }

    // 清空日志
    function clearLogs() {
        logContainer.innerHTML = '';
    }

    // 初始化页面
    document.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html> 